#include "EScene.h"
#include "EPhysics.h" 
#include "ECharacter.h"
#include "EPhController.h"

#include "eCPP.h"
namespace engine
{

//------------------------------------------------------------
// ESceneDesc
//------------------------------------------------------------
ESceneDesc::ESceneDesc() :
m_eShadowAlgorithm( ESceneDesc::SHADOW_ALGO_SHADOW_MAP ),
m_eShadowBlendMode( ESceneDesc::SHADOW_BLEND_MODE_ADDITIVE ),
m_cAmbientLight( 0.6f, 0.6f, 0.6f )
{

}

//------------------------------------------------------------
bool ESceneDesc::isValid() const
{
  cTEST( m_eShadowAlgorithm >= 0 && m_eShadowAlgorithm < SHADOW_ALGO_COUNT );
  cTEST( m_eShadowBlendMode >= 0 && m_eShadowBlendMode < SHADOW_BLEND_MODE_COUNT );

  return true;
}

//------------------------------------------------------------
// EScene
//------------------------------------------------------------
EScene::EScene(const ESceneDesc& desc, SceneManager& ogreSceneManager ) :
m_Desc( desc ),
m_pPhScene( NULL ),
m_pCurrentScreen( NULL ),
m_OgreSceneManager( ogreSceneManager )
{
  updateShadowParams();
}

//------------------------------------------------------------
EScene::~EScene()
{
  for( EScreenMap::iterator it=m_mapScreens.begin(); it != m_mapScreens.end(); ++it )
  {
    delete it->second;
  }
  m_mapScreens.clear();
}

//------------------------------------------------------------
bool EScene::update(const EGameTime& time )
{
  if( m_pCurrentScreen != NULL )
    cTEST( m_pCurrentScreen->update( time ) );

  cTEST( EEntityManager::update( time ) );

  return true;
}

//------------------------------------------------------------
void EScene::updateShadowParams()
{
  switch( m_Desc.m_eShadowAlgorithm )
  {
  case ESceneDesc::SHADOW_ALGO_VOLUMETRIC:
    {
      switch( m_Desc.m_eShadowBlendMode )
      {
      case ESceneDesc::SHADOW_BLEND_MODE_ADDITIVE:
        m_OgreSceneManager.setShadowTechnique( SHADOWTYPE_STENCIL_ADDITIVE );
        break;
      case ESceneDesc::SHADOW_BLEND_MODE_MODULATIVE:
        m_OgreSceneManager.setShadowTechnique( SHADOWTYPE_STENCIL_MODULATIVE );
        break;
      }
    } break;
  case ESceneDesc::SHADOW_ALGO_SHADOW_MAP:
    {
      switch( m_Desc.m_eShadowBlendMode )
      {
      case ESceneDesc::SHADOW_BLEND_MODE_ADDITIVE:
        m_OgreSceneManager.setShadowTechnique( SHADOWTYPE_TEXTURE_ADDITIVE );
        break;
      case ESceneDesc::SHADOW_BLEND_MODE_MODULATIVE:
        m_OgreSceneManager.setShadowTechnique( SHADOWTYPE_TEXTURE_MODULATIVE );
        break;
      }
    } break;
  }
}

//------------------------------------------------------------
void EScene::updateParams()
{
  updateShadowParams();
  m_OgreSceneManager.setAmbientLight( m_Desc.m_cAmbientLight );
}

//------------------------------------------------------------
bool EScene::startGame()
{
  cASSERT( m_pPhScene == NULL );
  cASSERT( g_pPhysics != NULL );
  m_pPhScene = g_pPhysics->createScene( m_Desc.m_PhSceneDesc );
  cASSERT( m_pPhScene != NULL );
  cTEST( m_pPhScene->init() );
  cTEST( initCharacterControllers() );

  if( m_pCurrentScreen != NULL )
    m_pCurrentScreen->startGame();

  cTEST( EEntityManager::startGame() );
  cTEST( EEventManager::startGame( *m_pPhScene ) );
  return true;
}

//------------------------------------------------------------
bool EScene::endGame()
{
  cTEST( EEventManager::endGame() );
  cTEST( EEntityManager::endGame() );

  if( m_pCurrentScreen != NULL )
    m_pCurrentScreen->endGame();

  cASSERT( m_pPhScene != NULL );
  releaseCharacterControllers();
  m_pPhScene->release();
  cASSERT( g_pPhysics != NULL );
  g_pPhysics->destroyScene( *m_pPhScene );
  m_pPhScene = NULL;
  return true;
}

//------------------------------------------------------------
bool EScene::updateGame(const EGameTime& time )
{
  if( m_pPhScene != NULL )
  {
    m_pPhScene->endSimulationComputing();
    m_pPhScene->startSimulationComputing( time );
  }

  cTEST( EEventManager::updateGame( time ) );

  if( m_pCurrentScreen != NULL )
    m_pCurrentScreen->updateGame( time );

  cTEST( EEntityManager::updateGame( time ) );

  return true;
}

//------------------------------------------------------------
bool EScene::startPhysicsSimulationComputing(const EGameTime& gameTime)
{
  cASSERT( m_pPhScene != NULL );
  return m_pPhScene->startSimulationComputing( gameTime );
}

//------------------------------------------------------------
bool EScene::endPhysicsSimulationComputing()
{
  cASSERT( m_pPhScene != NULL );
  return m_pPhScene->endSimulationComputing();
}

//------------------------------------------------------------
bool EScene::isPhysicsSimulationComputingEnded() const
{
  cASSERT( m_pPhScene != NULL );
  return m_pPhScene->isSimulationComputingEnded();
}

//------------------------------------------------------------
EScreen* EScene::createScreen(const string& sName, const EScreenDesc& screenDesc )
{
  if( !screenDesc.isValid() )
    return NULL;
  EScreen* pScreen = new EScreen( sName, screenDesc );
  cASSERT( pScreen != NULL );
  m_mapScreens[ sName ] = pScreen;

  return pScreen;
}

//------------------------------------------------------------
void EScene::destroyScreen(const string& sName )
{
  EScreenMap::iterator it = m_mapScreens.find( sName );
  cASSERT( it != m_mapScreens.end() );
  delete it->second;
  m_mapScreens.erase( sName );
}

//------------------------------------------------------------
ECharacter* EScene::createCharacter( const string& sName, const ECharacterDesc& charDesc )
{
  if( !charDesc.isValid() )
    return NULL;

  ECharacter *pCharacter = new ECharacter(sName, charDesc);
  cASSERT( pCharacter != NULL );
  addEntity(EEntity::ENTITY_GROUP_CHARACTER, *pCharacter );
  return pCharacter;
}

//------------------------------------------------------------
EStaticCollider* EScene::createStaticCollider( const EStaticColliderDesc& desc )
{
  if( !desc.isValid() )
    return NULL;

  EStaticCollider *pCollider = new EStaticCollider( desc );
  cASSERT( pCollider != NULL );
  addEntity(EEntity::ENTITY_GROUP_COLLIDERS, *pCollider );
  return pCollider;
}

//------------------------------------------------------------
bool EScene::setCurrentScreen(const string& sName )
{
  // Detach old screen
  if( m_pCurrentScreen != NULL )
  {
    m_pCurrentScreen->removeFromOgreScene( m_OgreSceneManager );
    m_pCurrentScreen = NULL;
  }
  if( sName == "" )
  {
    m_pCurrentScreen = NULL;
    return true;
  }
  EScreenMap::iterator it = m_mapScreens.find( sName );
  if( it == m_mapScreens.end() )
    return false;

  m_pCurrentScreen = it->second;
  cASSERT( m_pCurrentScreen != NULL );

  m_pCurrentScreen->addToOgreScene( m_OgreSceneManager );

  return true;
}

//------------------------------------------------------------
bool EScene::load()
{
  return true;
}

//------------------------------------------------------------
bool EScene::initCharacterControllers()
{
  cASSERT( m_pPhScene != NULL );
  EEntityGroup& group = getEntityGroup( EEntity::ENTITY_GROUP_CHARACTER );
  for( EEntityGroup::EEntityVector::iterator it = group.getIterator();
    it != group.getIteratorEnd(); it++ )
  {
    ECharacter* pCharacter = static_cast<ECharacter*>( *it );
    EPhController *pController = m_pPhScene->createController( pCharacter->getPhControllerDesc() );
    cASSERT( pController != NULL );
    pCharacter->setPhController( pController );
  }
  return true;
}

//------------------------------------------------------------
bool EScene::releaseCharacterControllers()
{
  cASSERT( m_pPhScene != NULL );
  EEntityGroup& group = getEntityGroup( EEntity::ENTITY_GROUP_CHARACTER );
  for( EEntityGroup::EEntityVector::iterator it = group.getIterator();
    it != group.getIteratorEnd(); it++ )
  {
    ECharacter* pCharacter = static_cast<ECharacter*>( *it );
    EPhController *pController = pCharacter->getPhControllerPtr();
    if( pController != NULL )
    {
      m_pPhScene->destroyController( *pController );
      pCharacter->setPhController( NULL );
    }
  }
  return true;
}

} // end namespace